<template>
  <el-drawer
    v-model="visible"
    :title="nodeDetail.method_name || nodeDetail.method"
    size="75%"
  >
    <div class="drawer-container">
      <div class="detail-content">
        <div class="api-info-section">
          <div class="info-item" @click="handleInfoItemClick(nodeDetail?.type || '')">
            <div class="label">请求方式</div>
            <div class="value">{{ nodeDetail?.type }}</div>
          </div>
          
          <div class="info-item" @click="handleInfoItemClick(nodeDetail?.router || '')">
            <div class="label">文件地址</div>
            <div class="value">{{ nodeDetail?.router }}</div>
          </div>
          
          <div class="info-item" @click="handleInfoItemClick(nodeDetail?.url || '')">
            <div class="label">接口URL</div>
            <div class="value">{{ nodeDetail?.url }}</div>
          </div>
          
          <div class="info-item" @click="handleInfoItemClick(JSON.stringify(parseJsonSafely(nodeDetail?.request_params), null, 2))">
            <div class="label">请求参数</div>
            <div class="json-viewer">
              <vue-json-pretty
                v-if="nodeDetail?.request_params"
                :data="parseJsonSafely(nodeDetail.request_params)"
                :deep="3"
                :showLength="true"
                :showLine="true"
                :showDoubleQuotes="true"
                :path="'res'"
                :selectableType="'single'"
                class="json-viewer-content"
              />
            </div>
          </div>
          
          <div class="info-item" @click="handleInfoItemClick(JSON.stringify(parseJsonSafely(nodeDetail?.response_params), null, 2))">
            <div class="label">响应参数</div>
            <div class="json-viewer">
              <vue-json-pretty
                v-if="nodeDetail?.response_params"
                :data="parseJsonSafely(nodeDetail.response_params)"
                :deep="3"
                :showLength="true"
                :showLine="true"
                :showDoubleQuotes="true"
                :path="'res'"
                :selectableType="'single'"
                class="json-viewer-content"
              />
            </div>
          </div>
        </div>
      </div>

      <div class="flow-content">
        <div id="container" ref="container"></div>
      </div>
    </div>
  </el-drawer>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import VueJsonPretty from 'vue-json-pretty'
import 'vue-json-pretty/lib/styles.css'
import { Graph, Cell, Node, Dom } from '@antv/x6'
import dagre from 'dagre'
import insertCss from 'insert-css'
import startImage from '../assets/start.png'
import successImage from '../assets/success.png'
import waitingImage from '../assets/waiting.png'
import { API_URL } from '@/utils/config'
import useClipboard from 'vue-clipboard3'

const props = defineProps<{
  modelValue: boolean
  nodeId: string
}>()

const emit = defineEmits(['update:modelValue', 'update:nodeId'])

const visible = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

// 定义节点和边的类型
interface NodeContent {
  name: string
  description: string
  nodeId?: string
  level?: string
}

const nodeDetail = ref<NodeContent[]>([])

const container = ref<HTMLElement | null>(null)
let graph: Graph | null = null

// 定义样式
insertCss(`
  .x6-cell {
    cursor: default;
  }
  .x6-node .btn {
    cursor: pointer;
  }
`)

// 注册自定义节点
Graph.registerNode(
  'org-node',
  {
    width: 180,
    height: 80,
    markup: [
      {
        tagName: 'rect',
        attrs: {
          class: 'card',
        },
      },
      {
        tagName: 'text',
        attrs: {
          class: 'rank',
        },
      },
      {
        tagName: 'text',
        attrs: {
          class: 'name',
        },
      },
      {
        tagName: 'text',
        attrs: {
          class: 'level',
        },
      },
    ],
    attrs: {
      '.card': {
        rx: 8,
        ry: 8,
        refWidth: '100%',
        refHeight: '100%',
        fill: '#f5f5f5',
        stroke: '#5F95FF',
        strokeWidth: 1,
        pointerEvents: 'visiblePainted',
        cursor: 'pointer',
      },
      '.rank': {
        refX: 12,
        refY: 0.25,
        fontSize: 15,
        fontFamily: 'Arial',
        textAnchor: 'start',
        textVerticalAnchor: 'middle',
      },
      '.name': {
        refX: 12,
        refY: 0.6,
        fontSize: 14,
        fontFamily: 'Arial',
        textAnchor: 'start',
        fill: '#666',
      },
      '.level': {
        refX: 0.95,
        refY: 0.25,
        fontSize: 14,
        fontFamily: 'Arial',
        textAnchor: 'end',
      },
    },
  },
  true,
)

Graph.registerEdge(
  'org-edge',
  {
    zIndex: -1,
    attrs: {
      line: {
        strokeWidth: 2,
        stroke: '#A2B1C3',
        sourceMarker: null,
        targetMarker: null,
      },
    },
  },
  true,
)

// 监听数据变化
watch(
  [() => props.nodeId, () => visible.value],
  async ([newNodeId, newVisible]) => {
    if (newVisible && newNodeId) {
      await fetchNodeDetail(newNodeId)
      await initGraph()
    }
  }
)

// 初始化图表
const initGraph = async () => {
  if (container.value && nodeDetail.value) {
    // 清除旧的图表实例
    if (graph) {
      graph.dispose()
    }

    const containerWidth = container.value.clientWidth
    const maxNodeWidth = Math.floor(containerWidth * 0.8) // 最大宽度为容器的80%

    graph = new Graph({
      container: container.value,
      interacting: false,
      panning: true,
      width: containerWidth,
      height: container.value.clientHeight,
    })

    // 添加点击事件监听
    graph.on('cell:click', ({ cell }) => {
      if (cell.isNode()) {
        const node = cell as Node
        const nodeData = node.getData()
        
        // 获取节点的文本内容
        const rankText = node.attr('.rank/text') as string
        const nameText = node.attr('.name/text') as string
        
        // 组合要复制的内容
        const textToCopy = `${rankText}\n${nameText}`.trim()
        
        // 复制到剪贴板
        copyToClipboard(textToCopy)
      }
    })

    // 创建节点函数
    function createNode(
      rank: string, 
      name: string, 
      image: string, 
      nodeId: string, 
      level: string, 
      isStart: boolean = false,
      index?: number
    ) {
      if (!graph) return null

      const containerWidth = graph.container.clientWidth
      const maxWidth = Math.floor(containerWidth * 0.8) // 容器宽度的80%
      const minWidth = 180 // 最小宽度

      // 创建 canvas 用于测量文本宽度
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      if (!ctx) return null

      // 设置字体以准确测量
      ctx.font = '15px Arial' // rank 的字体
      const rankWidth = ctx.measureText(rank).width
      
      ctx.font = '14px Arial' // name 的字体
      const nameWidth = ctx.measureText(name).width
      
      // 计算 level 的宽度(如果存在)
      const levelWidth = level ? ctx.measureText(level).width + 40 : 0 // 为 level 添加额外空间

      // 计算所需的最小宽度 (考虑内边距和最大宽度限制)
      const contentWidth = Math.max(
        rankWidth,
        nameWidth,
        levelWidth
      )
      
      // 最终宽度：内容宽度 + 左右内边距，且在最小和最大宽度之间
      const width = Math.min(
        maxWidth,
        Math.max(minWidth, contentWidth + 60) // 60px 为左右内边距总和
      )

      // 根据内容计算所需高度
      const padding = 20
      const gap = 16
      const lineHeight = 24

      // 计算文本换行
      const rankLines = splitTextIntoLines(rank, Math.floor((width - 40) / 8))
      const nameLines = splitTextIntoLines(name, Math.floor((width - 40) / 8))

      // 计算总高度
      const height = padding * 2 + // 上下内边距
        (rankLines.length * lineHeight) + // rank 文本高度
        gap + // rank 和 name 之间的间距
        (nameLines.length * lineHeight) // name 文本高度

      return graph.createNode({
        shape: 'org-node',
        width,
        height,
        attrs: {
          '.card': {
            rx: 8,
            ry: 8,
            refWidth: '100%',
            refHeight: '100%',
            fill: nodeId ? '#e6f7ff' : '#f5f5f5',
            stroke: '#5F95FF',
            strokeWidth: 1,
            pointerEvents: 'visiblePainted',
            cursor: 'pointer',
          },
          '.rank': {
            text: rankLines.join('\n'),
            fill: nodeId ? '#000000' : '#666666',
            fontSize: 15,
            textVerticalAnchor: 'top',
            textAnchor: 'start',
            refX: padding,
            refY: padding / height,
          },
          '.name': {
            text: nameLines.join('\n'),
            fill: nodeId ? '#000000' : '#666666',
            fontSize: 14,
            textVerticalAnchor: 'top',
            textAnchor: 'start',
            refX: padding,
            refY: (padding + rankLines.length * lineHeight + gap) / height,
          },
          '.level': {
            text: level || '-',
            fill: level === 'ERROR' ? 'red' : '#666666',
            fontSize: 14,
            textVerticalAnchor: 'middle',
            textAnchor: 'end',
            refX: width - padding,
            refY: padding / height,
          },
        },
        data: {
          nodeId,
          isStart,
          index,
        },
      })
    }

    const nodes: Array<ReturnType<typeof createNode>> = []
    
    // 创建起始节点
    nodes[0] = createNode('开始', 'Start', startImage, '', '', true)

    // 获取节点内容并进行类型检查
    const nodeContent = nodeDetail.value.content
    // 创建内容节点
    nodeContent.forEach((item, index) => {
      const node = createNode(
        item.name,
        item.description,
        '', // image
        item.nodeId || '',
        item.level || '',
        false,
        index  // 添加索引参数
      )
      if (node) nodes[index + 1] = node
    })
    nodes.push(createNode('结束', 'End', '', '', ''))

    // 创建边连接相邻节点
    const edges = nodes.reduce<Array<ReturnType<typeof createEdge>>>((acc, _, index) => {
      if (index < nodes.length - 1) {
        const edge = createEdge(nodes[index], nodes[index + 1])
        if (edge) acc.push(edge)
      }
      return acc
    }, [])

    // 重置图形元素
    if (nodes.length > 0) {
      graph.resetCells([...nodes, ...edges])
    }
    layout()
    graph.zoomTo(0.8)
    graph.centerContent()
  }
}

// 优化文本换行函数
function splitTextIntoLines(text: string, maxCharsPerLine: number): string[] {
  if (!text) return ['-']
  
  const lines: string[] = []
  let remaining = text.trim()
  
  while (remaining.length > 0) {
    if (remaining.length <= maxCharsPerLine) {
      lines.push(remaining)
      break
    }
    
    // 在最大长度位置查找最后一个空格
    let cutIndex = maxCharsPerLine
    const lastSpaceIndex = remaining.lastIndexOf(' ', maxCharsPerLine)
    
    // 如果找到了空格，就在空格处截断
    if (lastSpaceIndex > 0) {
      cutIndex = lastSpaceIndex
    }
    
    lines.push(remaining.substring(0, cutIndex))
    remaining = remaining.substring(cutIndex).trim()
  }

  return lines
}

// 创建边函数
function createEdge(source: Cell, target: Cell) {
  if (!graph || !source || !target) return null

  return graph.createEdge({
    shape: 'org-edge',
    source: { cell: source.id },
    target: { cell: target.id },
  })
}

// 修改布局函数
function layout() {
  if (!graph) return

  const nodes = graph.getNodes()
  const edges = graph.getEdges()
  const g = new dagre.graphlib.Graph()
  
  // 减小节点间距
  g.setGraph({ 
    rankdir: 'TB',     // 从上到下布局
    nodesep: 50,       // 同一层级节点之间的间距（减小）
    ranksep: 10,       // 不同层级之间的间距（减小）
    align: 'UL',       // 向上和向左对齐
    marginx: 30,       // 图形左右边距（减小）
    marginy: 30        // 图形上下边距（减小）
  })
  g.setDefaultEdgeLabel(() => ({}))

  // 添加节点
  nodes.forEach((node) => {
    const { width, height } = node.size()
    // 为每个节点添加额外的边距
    g.setNode(node.id, { 
      width: width + 20,  // 添加水平边距
      height: height + 20 // 添加垂直边距
    })
  })

  // 添加边
  edges.forEach((edge) => {
    const source = edge.getSource()
    const target = edge.getTarget()
    if (source && target) {
      g.setEdge(source.cell, target.cell)
    }
  })

  // 执行布局
  dagre.layout(g)

  // 设置节点位置
  g.nodes().forEach((id) => {
    const node = graph.getCellById(id) as Node
    if (node) {
      const pos = g.node(id)
      // 考虑边距，调整最终位置
      node.position(
        pos.x - pos.width / 2 + 10,  // 水平居中并考虑边距
        pos.y - pos.height / 2 + 10   // 垂直居中并考虑边距
      )
    }
  })

  // 优化边的连接
  edges.forEach((edge) => {
    const source = edge.getSourceNode()
    const target = edge.getTargetNode()
    if (source && target) {
      const sourceBBox = source.getBBox()
      const targetBBox = target.getBBox()

      // 计算连接点
      const sourcePoint = {
        x: sourceBBox.center.x,
        y: sourceBBox.bottom
      }
      const targetPoint = {
        x: targetBBox.center.x,
        y: targetBBox.top
      }

      // 设置连接线的路径点
      const midY = (sourcePoint.y + targetPoint.y) / 2
      edge.setVertices([
        { x: sourcePoint.x, y: midY },
        { x: targetPoint.x, y: midY }
      ])
    }
  })

  // 调整画布以适应所有内容
  graph.centerContent()
  graph.zoomToFit({ padding: 50 })
}

// 获取节点详情数据
const fetchNodeDetail = async (nodeId: string) => {
  try {
    const response = await fetch(`${API_URL}/logs/node_detail/${nodeId}`)
    const data = await response.json()
    if (data.code === 0 && data.data) {
      nodeDetail.value = data.data
    } else {
      ElMessage.error('获取节点详情失败')
    }
  } catch (error) {
    ElMessage.error('获取节点详情失败')
  }
}

const parseJsonSafely = (value: any): any => {
  if (!value) return {}
  if (typeof value === 'string') {
    try {
      return JSON.parse(value)
    } catch (e) {
      return value
    }
  }
  return value
}

const copyToClipboard = async (text: string) => {
  try {
    const { toClipboard } = useClipboard()
    await toClipboard(text);
    ElMessage({
      message: '内容已复制到剪贴板',
      type: 'success',
      duration: 2000
    })
  } catch (err) {
    ElMessage({
      message: '复制失败，请重试',
      type: 'error',
      duration: 2000
    })
  }
}

// 在script部分添加新的点击处理函数
const handleInfoItemClick = (content: string) => {
  copyToClipboard(content)
}
</script>

<style scoped>
.drawer-container {
  height: 100%;
  display: flex;
}

.detail-content {
  flex: 1;
  padding: 20px;
  border-right: 1px solid #f0f0f0;
  overflow-y: auto;
}

.flow-content {
  flex: 1;
  padding: 20px;
  height: 100%;
}

#container {
  height: 100%;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.api-info-section {
  padding: 20px;
}

.info-item {
  margin-bottom: 24px;
  position: relative;
  cursor: pointer;
  transition: all 0.3s ease;
}

.info-item:hover {
  opacity: 0.8;
}

.info-item .label {
  font-weight: 600;
  margin-bottom: 8px;
  color: #333;
  font-size: 14px;
}

.info-item .value {
  background: #f5f7fa;
  padding: 8px 12px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 13px;
  color: #444;
  word-break: break-all;
}

.json-viewer {
  background: #f5f7fa;
  padding: 16px;
  border-radius: 4px;
  margin-top: 8px;
  max-height: 400px;
  overflow-y: auto;
}

.json-viewer-content {
  font-size: 13px !important;
}

:deep(.vjs-tree) {
  font-size: 13px;
  font-family: monospace;
  background-color: transparent !important;
}

:deep(.vjs-tree .vjs-value__string) {
  color: #c41a16 !important;
}

:deep(.vjs-tree .vjs-value__number) {
  color: #1a1aa6 !important;
}

:deep(.vjs-tree .vjs-value__boolean) {
  color: #0000ff !important;
}

:deep(.vjs-tree .vjs-value__null) {
  color: #808080 !important;
}

:deep(.vjs-tree .vjs-key) {
  color: #881391 !important;
}

:deep(.vjs-tree .vjs-value__undefined) {
  color: #808080 !important;
}
</style> 